﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DynaPrecision.Financial
{
	/// <summary>
	/// 
	/// </summary>
	public class ThetaProjectedSOREvaluator : TransformedFiniteDifferenceScheme
	{
		public double Theta { get; set; }
		public double SORConstant { get; set; }
		public ThetaProjectedSOREvaluator()
		{
			Theta = 0.5;
			MaximalIterationCount = 500;
			ErrorLimit = 1.0e-20;
			SORConstant = 1.0;
		}
		public Func<double, double, double> UpperBoundValue
		{
			get
			{
				return InitialAndBoundaryValueProvider.UpperBoundValue;
			}
		}
		public Func<double, double, double> LowerBoundValue
		{
			get
			{
				return InitialAndBoundaryValueProvider.LowerBoundValue;
			}
		}
		MemorylessTimeStepHolder _timeStepHolder = new MemorylessTimeStepHolder();
		public override void Initiate()
		{
			base.Initiate();
			CurrentTransformedTime = 0;
			if (InitialAndBoundaryValueProvider == null) throw new Exception("Provide boundary and/or initial conditions");
			_timeStepHolder.DataSize = this.TransformedPriceDivisionCount + 1;
			_timeStepHolder.Initiate();
			_rightHandSide = DynaPrecision.ListGenerator.ConstantValueList<double>(0, TransformedPriceDivisionCount + 1).ToList();
			_sorCache = DynaPrecision.ListGenerator.ConstantValueList<double>(0, TransformedPriceDivisionCount + 1).ToList();
			for (int i = 0; i < this.TransformedPriceDivisionCount + 1; i++)
			{
				_timeStepHolder.CurrentTimeStep[i] = _initialAandBoundaryValueProvider.Payoff(_priceList[i], this.Expiration) / this.ValueTransformMultiplier(_transformedPriceList[i], 0);
			}
			if (IsAmerican)
			{
				_payoff = DynaPrecision.ListGenerator.ConstantValueList<double>(0, TransformedPriceDivisionCount + 1).ToList();
			}
		}
		double CurrentTransformedTime = 0;
		public void AdvanceToFinish()
		{
			for (int i = 0; i < TransformedTimeDivisionCount; i++)
			{
				Advance();
			}
		}
		public void Advance()
		{
			CurrentTransformedTime += TransformedTimeDivision;
			_refreshRightHandSide();
			if (IsAmerican) _refreshPayoff();
			for (int i = 1; i < TransformedPriceDivisionCount; i++)
			{
				if (IsAmerican)
				{
					_timeStepHolder.NextTimeStep[i] = _timeStepHolder.CurrentTimeStep[i] > _payoff[i] ? _timeStepHolder.CurrentTimeStep[i] : _payoff[i];
				}
				else
				{
					_timeStepHolder.NextTimeStep[i] = _timeStepHolder.CurrentTimeStep[i];
				}
			}
			_timeStepHolder.NextTimeStep[0] = _initialAandBoundaryValueProvider.LowerBoundValue(PriceLowerBound, DeTransformTime(CurrentTransformedTime)) / ValueTransformMultiplier(_transformedPriceList[0], CurrentTransformedTime);
			_timeStepHolder.NextTimeStep[TransformedPriceDivisionCount] = _initialAandBoundaryValueProvider.UpperBoundValue(PriceUpperBound, DeTransformTime(CurrentTransformedTime)) / ValueTransformMultiplier(_transformedPriceList[0], CurrentTransformedTime);
			double error = 0;
			for (int i = 0; i < MaximalIterationCount; i++)
			{
				error = _SOR_SingleIteration();
				if (error <= ErrorLimit)
				{
					break;
				}
			}
			if (error > ErrorLimit)
			{
				throw new Exception("Linear Solver cannot converge");
			}
			this._timeStepHolder.Advance();

		}
		public double ErrorLimit
		{
			get;
			set;
		}
		public int MaximalIterationCount
		{
			get;
			set;
		}
		public IEnumerable<DynaPrecision.Pair<double, double>> Result
		{
			get
			{
				for (int i = 0; i < TransformedPriceDivisionCount + 1; i++)
				{
					//yield return new DynaPrecision.Pair<double, double> { LeftValue = _transformedPriceList[i], RightValue = _timeStepHolder.CurrentTimeStep[i] };
					yield return new DynaPrecision.Pair<double, double> { LeftValue = _priceList[i], RightValue = _timeStepHolder.CurrentTimeStep[i] * this.ValueTransformMultiplier(_transformedPriceList[i], TransformedExpiration) };
					//yield return new DynaPrecision.Pair<double, double> { LeftValue = _priceList[i], RightValue = _rightHandSide[i] };
				}
			}
		}
		protected void _refreshRightHandSide()
		{
			for (int n = 1; n < TransformedPriceDivisionCount; n++)
			{
				_rightHandSide[n] = (1 - 2 * Theta * TimePriceSquaredDivisionRatio) * _timeStepHolder.CurrentTimeStep[n]
				+ Theta * TimePriceSquaredDivisionRatio * (_timeStepHolder.CurrentTimeStep[n + 1] + _timeStepHolder.CurrentTimeStep[n - 1]);
			}
		}
		protected IList<double> _rightHandSide = null;
		protected void _refreshPayoff()
		{
			var currentTime = this.DeTransformTime(CurrentTransformedTime);
			for (int i = 0; i < TransformedPriceDivisionCount + 1; i++)
			{
				_payoff[i] = _initialAandBoundaryValueProvider.Payoff(_priceList[i], currentTime) / this.ValueTransformMultiplier(_transformedPriceList[i], CurrentTransformedTime);
			}
		}
		protected IList<double> _payoff = null;
		protected IList<double> _sorCache = null;
		protected double _SOR_SingleIteration()
		{
			for (int i = 1; i < TransformedPriceDivisionCount; i++)
			{
				_sorCache[i] = _timeStepHolder.NextTimeStep[i];
			}
			for (int i = 1; i < TransformedPriceDivisionCount; i++)
			{
				_timeStepHolder.NextTimeStep[i] = _rightHandSide[i];
				_timeStepHolder.NextTimeStep[i] += (1 - Theta) * TimePriceSquaredDivisionRatio * (_timeStepHolder.NextTimeStep[i - 1] + _timeStepHolder.NextTimeStep[i + 1]);
				_timeStepHolder.NextTimeStep[i] /= (1 + 2 * (1 - Theta) * TimePriceSquaredDivisionRatio);
				_timeStepHolder.NextTimeStep[i] = _timeStepHolder.NextTimeStep[i] * SORConstant + (1 - SORConstant) * _sorCache[i];
			}
			if (IsAmerican)
			{
				for (int i = 1; i < TransformedPriceDivisionCount; i++)
				{
					_timeStepHolder.NextTimeStep[i] = _timeStepHolder.NextTimeStep[i] > _payoff[i] ? _timeStepHolder.NextTimeStep[i] : _payoff[i];
				}
				
			}
			for (int i = 1; i < TransformedPriceDivisionCount; i++)
			{
				_sorCache[i] -= _timeStepHolder.NextTimeStep[i];
			}
			return _sorCache.Sum(_x=>Math.Abs(_x));
		}
	}
}